#include "texture2d.h"

#include <assert.h>

namespace GL
{

Texture2D::Texture2D()
: m_Id(0)
, m_Width(0)
, m_Height(0)
, m_MinFilter(GL_LINEAR)
, m_MagFilter(GL_LINEAR)
, m_WraptModeS(GL_CLAMP_TO_EDGE)
, m_WraptModeT(GL_CLAMP_TO_EDGE)
, m_InternalFormat(0)
, m_Border(0)
, m_TexUnit(GL_TEXTURE0)
{
    glGenTextures(1, &m_Id);
}
    
Texture2D::~Texture2D()
{
    glDeleteTextures(1, &m_Id);
}

GLuint Texture2D::getId() const
{
    return m_Id;
}

void Texture2D::bind() const
{
    glEnable(GL_TEXTURE_2D);
    glActiveTexture(m_TexUnit);
    glBindTexture(GL_TEXTURE_2D, m_Id);
}

void Texture2D::unbind()
{
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    glActiveTexture(GL_TEXTURE0);
    glDisable(GL_TEXTURE_2D);
}

void Texture2D::setFormat(GLint internalFormat, GLint format, GLenum type)
{
    m_InternalFormat = internalFormat;
    m_Format = format;
    m_Type = type;
}

void Texture2D::create(GLint minFilter, GLint magFilter,
                       GLint wraptModeS, GLint wraptModeT,
                       GLint internalFormat, GLsizei width, GLsizei height,
                       GLint border, GLint format, GLenum type,
                       GLvoid* pixels)
{
    m_MinFilter     = minFilter;
    m_MagFilter     = magFilter;
    m_WraptModeS    = wraptModeS;
    m_WraptModeT    = wraptModeT;
    m_InternalFormat= internalFormat;
    m_Border        = border;
    m_Format        = format;
    m_Type          = type;
    
    resize(width, height, pixels);
        
    assert(glGetError() == 0);
}

void Texture2D::setTextureUnit(GLenum unit)
{
    m_TexUnit = unit;
}

void Texture2D::setTextureData(GLvoid* pixels)
{
    assert(m_Width && m_Height && m_Id);
    glPushAttrib(GL_ENABLE_BIT);
    glEnable(GL_TEXTURE_2D);
    
    GLenum prevUnit = activateTextureUnit(m_TexUnit);
    GLint prevTex = bindTexture(m_Id);
    
    glBindTexture(GL_TEXTURE_2D, m_Id);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_Width, m_Height, m_Format, m_Type, pixels);
    
    glBindTexture(GL_TEXTURE_2D, prevTex);
    glActiveTexture(prevUnit);
    glPopAttrib();
    assert(glGetError() == 0);
}

void Texture2D::resize(GLsizei width, GLsizei height, GLvoid* pixels)
{
    glPushAttrib(GL_ENABLE_BIT);
    glEnable(GL_TEXTURE_2D);
    
    assert(m_InternalFormat);
    glDeleteTextures(1, &m_Id);
    glGenTextures(1, &m_Id);
    
    m_Width = width;
    m_Height = height;
    
    GLenum prevUnit = activateTextureUnit(m_TexUnit);
    GLint prevTex = bindTexture(m_Id);
    
    glBindTexture(GL_TEXTURE_2D, m_Id);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_MinFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_MagFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_WraptModeS);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_WraptModeT);
    glTexImage2D(GL_TEXTURE_2D, 0, m_InternalFormat, m_Width, m_Height, m_Border, m_Format, m_Type, pixels);
    
    glBindTexture(GL_TEXTURE_2D, prevTex);    
    glActiveTexture(prevUnit);
    glPopAttrib();
    assert(glGetError() == 0);
}

GLint Texture2D::bindTexture(GLuint tex)
{
    GLint prevTex;
    glGetIntegerv(GL_TEXTURE_BINDING_2D, &prevTex);
    
    glBindTexture(GL_TEXTURE_2D, tex);
    return prevTex;
}

GLenum Texture2D::activateTextureUnit(GLenum unit)
{
    GLint prevActive;
    glGetIntegerv(GL_ACTIVE_TEXTURE, &prevActive);
    
    glActiveTexture(unit);
    return prevActive;
}
                   
}
